package com.startdima.user.application.service;

import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;

import com.startdima.user.domain.entity.UserInfo;
import com.startdima.user.domain.repository.UserRepository;
import com.startdima.user.infrastructure.dto.SddmResult;
import com.startdima.user.infrastructure.enums.LocaleEnum;
import com.startdima.user.infrastructure.exception.BusinessException;
import com.startdima.user.infrastructure.mapper.UserMapper;
import com.startdima.user.infrastructure.utils.JwtTokenUtil;
import com.startdima.user.infrastructure.vo.LoginVo;
import com.startdima.user.infrastructure.vo.UserInfoAddOrUpdateVo;
import com.startdima.user.infrastructure.vo.UserInfoVo;
import com.startdima.user.infrastructure.vo.UserQueryVo;

import lombok.NonNull;

@Service
public class UserService {
	
	@Autowired
	private JwtTokenUtil jwtTokenUtil;
	
	@Autowired
	private UserRepository userRepository;

	/**
	 * 用户登陆
	 * @param vo 用户信息
	 * @param response 响应
	 * @return SddmResult
	 */
    public SddmResult loginFromApp(LoginVo vo, HttpServletResponse response) {
    	Optional<UserInfo> userInfoOptional = userRepository.findByUsernameAndPassword(vo.getUserName(), vo.getPassword());
    	// 用户存在
    	if (userInfoOptional.isPresent()) {
    		// 生成Token
    		String token = jwtTokenUtil.generateToken(userInfoOptional.get());
    		// 更新用户Token信息
    		updateUserToken(userInfoOptional.get(), token);
    		// 返回Header：Authorization，对应Token值
    		response.addHeader(HttpHeaders.AUTHORIZATION, token);
    		return new SddmResult(token);
		} else {
			// 用户不存在
			return new SddmResult(false, vo.getUserName() + ":" + LocaleEnum.USER_PASSWORD_WRONG.name());
		}
    }
    
    /**
     * 保存用户信息
     * @param userInfo 用户信息
     * @return UserInfoVo
     */
    public UserInfoVo save (UserInfoAddOrUpdateVo userInfo) {
    	checkUsernameUnique(userInfo.getUsername());
    	UserInfo userEntityInfo = UserMapper.INSTANCE.toUserInfo(userInfo);
    	userEntityInfo = userRepository.save(userEntityInfo);
    	return UserMapper.INSTANCE.toUserInfoVo(userEntityInfo);
    }
    
    /**
     * 修改用户信息
     * @param userId 用户主键
     * @param updateUserInfo 用户信息
     * @return UserInfoVo
     */
    public UserInfoVo update(Long userId, UserInfoAddOrUpdateVo updateUserInfo) {
    	UserInfo beforeUserInfo = findByIdOrThrow(userId);
    	if (!Objects.equals(beforeUserInfo.getUsername(), updateUserInfo.getUsername())) {
    		checkUsernameUnique(updateUserInfo.getUsername());
		}
    	UserInfo userEntityInfo = UserMapper.INSTANCE.updateUserInfo(updateUserInfo, beforeUserInfo);
    	userEntityInfo = userRepository.save(userEntityInfo);
    	return UserMapper.INSTANCE.toUserInfoVo(userEntityInfo);
    }
    
    private void checkUsernameUnique(String username) {
    	Optional<UserInfo> userOptional = userRepository.findByUsername(username);
    	if (userOptional.isPresent()) {
			throw new BusinessException("用户名：" + userOptional.get().getUsername() + ",已经被使用过了");
		}
    }
    
    /**
     * 删除用户
     * @param userId 用户ID
     */
    public void delete(@NonNull Long userId) {
    	userRepository.deleteById(userId);
    }
    
    /**
     * 分页查询
     * @param userQueryVo 查询条件
     * @return Page<UserInfoVo>
     */
    public Page<UserInfoVo> search(UserQueryVo userQueryVo) {
    	Specification<UserInfo> specification = genSpecification(userQueryVo);
    	Pageable pageable = PageRequest.of(userQueryVo.getPage() - 1, userQueryVo.getRows());
    	Page<UserInfo> pageDataPage = userRepository.findAll(specification, pageable);
    	List<UserInfoVo> userInfoVos = pageDataPage.getContent().stream().map(UserMapper.INSTANCE::toUserInfoVo).collect(Collectors.toList());
    	return new PageImpl<UserInfoVo>(userInfoVos, pageable, pageDataPage.getTotalElements());
    }
    
    private Specification<UserInfo> genSpecification(UserQueryVo userQueryVo) {
        return (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new LinkedList<>();
            if (StringUtils.isNotEmpty(userQueryVo.getUsername())) {
                predicates.add(criteriaBuilder.like(root.get("username"), '%' + userQueryVo.getUsername() + '%'));
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
    }
    
    /**
     * 通过主键查到用户信息
     * @param userId 用户主键
     * @return UserInfo
     */
    private UserInfo findByIdOrThrow(@NonNull Long userId) {
    	Optional<UserInfo> userInfOptional = userRepository.findById(userId);
    	if (userInfOptional.isPresent()) {
			return userInfOptional.get();
		}
    	throw new BusinessException(userId + "查无此用户");
    }
    
    /**
     * 根据Token获取用户信息
     * @param token Token
     * @return UserInfo 用户信息
     */
    public UserInfo getUserByToken(String token) {
    	Optional<UserInfo> userOptional = userRepository.findByToken(token);
    	if (userOptional.isPresent()) {
    		return userOptional.get();
		}
    	throw new BusinessException("无效Token值");
	}
    
    /**
     * 更新用户Token信息
     * @param userInfo 用户信息
     * @param token Token
     */
    private void updateUserToken(UserInfo userInfo, String token) {
    	userInfo.setToken(token);
    	userRepository.saveAndFlush(userInfo);
	}

}